package com.iwhalecloud.bss.kite.cucc.service.batch;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.iwhalecloud.bss.kite.client.dto.DataResult;
import com.iwhalecloud.bss.kite.common.constant.ErrorConsts;
import com.iwhalecloud.bss.kite.common.constant.KeyConsts;
import com.iwhalecloud.bss.kite.common.util.KiteBeanUtils;
import com.iwhalecloud.bss.kite.common.util.KiteListUtils;
import com.iwhalecloud.bss.kite.common.util.KiteMapUtils;
import com.iwhalecloud.bss.kite.common.util.KiteObjectUtils;
import com.iwhalecloud.bss.kite.common.util.KiteStringUtils;
import com.iwhalecloud.bss.kite.common.util.SeqUtils;
import com.iwhalecloud.bss.kite.common.util.TransactionUtils;
import com.iwhalecloud.bss.kite.cucc.client.api.batch.IBatchAcceptService;
import com.iwhalecloud.bss.kite.cucc.client.api.batch.IBatchOrderNodeService;
import com.iwhalecloud.bss.kite.cucc.client.api.inf.IInfServiceBus;
import com.iwhalecloud.bss.kite.cucc.client.api.order.IOrderSignService;
import com.iwhalecloud.bss.kite.cucc.client.api.reception.ICustReceptionService;
import com.iwhalecloud.bss.kite.cucc.client.api.scene.ICuccSceneService;
import com.iwhalecloud.bss.kite.cucc.client.api.scene.IInitSceneOfferInstService;
import com.iwhalecloud.bss.kite.cucc.client.api.scene.ISceneCommitService;
import com.iwhalecloud.bss.kite.cucc.client.api.shoppingcart.operate.ICuccOperateShoppingCartService;
import com.iwhalecloud.bss.kite.cucc.client.api.shoppingcart.query.ICuccQueryShoppingCartService;
import com.iwhalecloud.bss.kite.cucc.client.dto.batch.KiteBatchAccept;
import com.iwhalecloud.bss.kite.cucc.client.dto.batch.KiteBatchAcceptDetail;
import com.iwhalecloud.bss.kite.cucc.client.dto.batch.KiteBatchAcceptDetailDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.cust.CuccContextCustDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosGoodsDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosGoodsFeeDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosGoodsPayRealtionDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.scene.CuccCustInfoDetailDTO;
import com.iwhalecloud.bss.kite.cucc.client.vo.batch.WorksheetMergeVO;
import com.iwhalecloud.bss.kite.cucc.common.constant.LocalKeyConsts;
import com.iwhalecloud.bss.kite.cucc.dataservice.cache.ExtOrderInfoCache;
import com.iwhalecloud.bss.kite.cucc.dataservice.mapper.batch.KiteBatchAcceptDetailMapper;
import com.iwhalecloud.bss.kite.cucc.dataservice.mapper.batch.KiteBatchAcceptMapper;
import com.iwhalecloud.bss.kite.cucc.manager.inst.CuccKiteOfferInstAcct;
import com.iwhalecloud.bss.kite.cucc.manager.scene.service.operate.LocalOperateOfferInstAcctRelService;
import com.iwhalecloud.bss.kite.cucc.manager.scene.service.operate.LocalOperateOfferInstDiscountService;
import com.iwhalecloud.bss.kite.cucc.service.cache.CuccContextCustCache;
import com.iwhalecloud.bss.kite.cucc.service.context.CuccServiceContext;
import com.iwhalecloud.bss.kite.cucc.service.pos.handler.IKitePosServiceHandler;
import com.iwhalecloud.bss.kite.dataservice.util.IdUtils;
import com.iwhalecloud.bss.kite.manager.api.IOperateSceneInstService;
import com.iwhalecloud.bss.kite.manager.api.IQuerySceneInstService;
import com.iwhalecloud.bss.kite.manager.dto.KiteAttrDTO;
import com.iwhalecloud.bss.kite.manager.enums.ActionType;
import com.iwhalecloud.bss.kite.manager.enums.InstType;
import com.iwhalecloud.bss.kite.manager.enums.RelType;
import com.iwhalecloud.bss.kite.manager.inst.KiteAttr;
import com.iwhalecloud.bss.kite.manager.inst.KiteOfferInst;
import com.iwhalecloud.bss.kite.manager.inst.KiteOfferInstContract;
import com.iwhalecloud.bss.kite.manager.inst.KiteOfferInstDiscount;
import com.iwhalecloud.bss.kite.manager.inst.KiteProdInst;
import com.iwhalecloud.bss.kite.manager.inst.KiteSceneInst;
import com.iwhalecloud.bss.kite.manager.util.KiteTransactionUtils;
import com.iwhalecloud.bss.kite.service.context.ServiceContext;
import com.iwhalecloud.bss.kite.service.inf.accept.kitexchange.handler.AcceptScalpelUtil;
import com.ztesoft.bss.common.bo.LoginInfo;
import com.ztesoft.bss.common.bo.SimpleLoginInfo;
import com.ztesoft.bss.common.bo.UserInfo;
import com.ztesoft.bss.common.util.ContextUtil;
import com.ztesoft.bss.common.util.SpringUtil;
import com.ztesoft.zsmart.core.log.ZSmartLogger;

import lombok.RequiredArgsConstructor;
import tk.mybatis.mapper.entity.Example;

/**
 * @author lyx
 * @version V1.0
 * @Description:
 * @date 2021/11/29 0029
 */
@Service
@RequiredArgsConstructor
public class BatchOrderNodeService implements IBatchOrderNodeService {

    private static final ZSmartLogger LOGGER = ZSmartLogger.getLogger(BatchOrderNodeService.class);

    private final IInfServiceBus infServiceBus;

    private final KiteBatchAcceptMapper kiteBatchAcceptMapper;

    private final KiteBatchAcceptDetailMapper kiteBatchAcceptDetailMapper;

    private final IQuerySceneInstService querySceneInstService;

    private final ICuccSceneService cuccSceneService;

    private final IOperateSceneInstService operateSceneInstService;

    private final LocalOperateOfferInstAcctRelService operateOfferInstAcctRelService;

    private final LocalOperateOfferInstDiscountService operateOfferInstDiscountService;

    private final IKitePosServiceHandler posServiceHandler;

    private final ISceneCommitService sceneCommitService;

    private final IBatchAcceptService batchAcceptService;

    private final IOrderSignService orderSignService;

    private final IInitSceneOfferInstService initSceneOfferInstService;

    private final LocalBatchAcceptBO localBatchAcceptBO;

    @Override
    public Map<String, Object> getOrderInfo(String orderId, String flowId, String preTaskCode, String serviceOfferId,
        String staffId, String lanId, String regionId, Map<String, Object> dataMap) {
        if (!KiteStringUtils.equalsAny(preTaskCode, "batchSIGN", "batchOrderStartRent")) {
            return null;
        }
        Map<String, Object> rspMap = infServiceBus.queryWorkSheetsMergeInf(orderId, flowId);
        Map<String, Object> rspDataMap = KiteMapUtils.getValue(rspMap, "data");
        if (!KiteStringUtils.equals("0", KiteMapUtils.getString(rspMap, "status"))) {
            return null;
        }
        String rootOrderId = KiteMapUtils.getString(rspDataMap, "rootOrderId");
        if (KiteStringUtils.isEmpty(serviceOfferId)) {
            serviceOfferId = "1";
        }
        // 主单没有flowId，根据订单详情查询的结果渲染页面
        Map<String, Object> orderDetailMap =
            this.cuccSceneService.getOfferSceneDataByOrderDetailInfo(rootOrderId, serviceOfferId,
                KeyConsts.KITE_SCENE_INST_OPER_TYPE_1000, "", "", staffId, lanId, regionId).getResultObject();
        dataMap.replace("kitePosDTO", KiteMapUtils.getValue(orderDetailMap, "kitePosDTO"));
        return rspDataMap;
    }

    @Override
    public Map<String, Object> getOrderInfoSync(String orderId, String flowId, String preTaskCode, String serviceOfferId) {
        if (!KiteStringUtils.equalsAny(preTaskCode, "batchSIGN", "batchOrderStartRent")) {
            return null;
        }
        Map<String, Object> rspMap = infServiceBus.queryWorkSheetsMergeInf(orderId, flowId);
        Map<String, Object> rspDataMap = KiteMapUtils.getValue(rspMap, "data");
        if (!KiteStringUtils.equals("0", KiteMapUtils.getString(rspMap, "status"))) {
            return null;
        }
        return rspDataMap;
    }

    @Override
    public boolean batchSignJob() {
        StopWatch stopWatch = new StopWatch("sign");
        stopWatch.start("sign");

        batchOrderTask(LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7100, "ORDER_SIGNED");

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
        return Boolean.TRUE;
    }

    private String createScene(JSONObject orderInfo, KiteBatchAcceptDetailDTO detailDTO, KiteSceneInst mainSceneInst, LoginInfo loginInfo) {
        String serviceOfferId = mainSceneInst.getServiceOfferId();
        String custId = mainSceneInst.getCustId();
        String custName = mainSceneInst.getCustName();
        String postProvinceNbr = mainSceneInst.getProvinceNbr();
        String operType = mainSceneInst.getOperType();
        String orderId = KiteMapUtils.getString(orderInfo, "orderId");
        String flowId = KiteMapUtils.getString(orderInfo, "flowId");
        String taskId = KiteMapUtils.getString(orderInfo, "taskId");
        String staffId = String.valueOf(detailDTO.getCreateStaff());
        String regionId = String.valueOf(detailDTO.getRegionId());
        String lanId = String.valueOf(detailDTO.getLanId());
        // 获取工单信息
        Map<String, Object> dataMap = cuccSceneService.getOfferSceneDataByWorkOrder(orderId, flowId, taskId, serviceOfferId,
            operType, "", "", ContextUtil.getUserId().toString(), ContextUtil.getLandId(),
            regionId, null).getResultObject();

        CuccServiceContext.setIsCallInfForInitAttr(Boolean.FALSE);

        Map<String, Object> loginExtParams = loginInfo.getUserInfo().getExtParams();
        // 特殊参数,用于创建场景后设置场景参数用,该方法结束前移除
        String nodeCode = KiteMapUtils.getString(dataMap, "nodeCode");
        loginExtParams.put("nodeCode", nodeCode);
        loginExtParams.put("serviceOfferId", KiteMapUtils.getString(dataMap, "serviceOfferId"));
        loginExtParams.put("goodsNodeId", KiteMapUtils.getString(dataMap, "goodsNodeId"));

        String newServiceOfferId = KiteMapUtils.getString(dataMap, "serviceOfferId");
        KitePosDTO kitePosDTO = (KitePosDTO) KiteMapUtils.getObject(dataMap, "kitePosDTO");

        // 获取其它业务参数
        String upServiceOfferId = KiteMapUtils.getString(dataMap, "upServiceOfferId");
        List<String> serviceOfferIdList = KiteMapUtils.getValue(dataMap, "serviceOfferIdList");
        Map<String, Object> worksheetData = KiteMapUtils.getValue(dataMap, "worksheetData");
        JSONObject orderData = (JSONObject) KiteMapUtils.getObject(dataMap, "orderData");

        loginExtParams.put("orderSource", KiteMapUtils.getString(worksheetData, "orderSource"));
        // 创建场景
        String sceneInstId = this.cuccSceneService.createOfferScene(newServiceOfferId, operType, LocalKeyConsts.ASK_SOURCE_ORDER,
            custId, custName, staffId, lanId, regionId, kitePosDTO).getResultObject();
        // 还原属性值
        this.cuccSceneService.initModifySceneAttrValues(sceneInstId, operType, custId, staffId, orderId, newServiceOfferId,
            upServiceOfferId, serviceOfferIdList, worksheetData, kitePosDTO, orderData);
//        this.cuccSceneService.initMoreSceneAttrValues(sceneInstId, custId, staffId, postProvinceNbr, kitePosDTO);
        loginExtParams.remove("nodeCode");
        loginExtParams.remove("serviceOfferId");
        loginExtParams.remove("orderSource");

        return sceneInstId;
    }

    private void batchOrderTask(String importType, String dealResult) {
        List<KiteBatchAcceptDetailDTO> detailDTOList =
            BatchAcceptUtil.getBatchAcceptDetails(Lists.newArrayList(importType));
        if (KiteListUtils.isEmpty(detailDTOList)) {
            return;
        }
        try {
            Map<String, LoginInfo> loginInfoMap = new HashMap<>();
            Map<String, CuccContextCustDTO> custInfoMap = new HashMap<>();
            ICustReceptionService custReceptionService = SpringUtil.getBean(ICustReceptionService.class);
            // 自定义线程池
            ExecutorService executorService = BatchJobThreadPoolUtil.getInstance().getThreadPool();
            // 拆分list，每20条一个list，单独开启一个事务
            List<List<KiteBatchAcceptDetailDTO>> partitionList = Lists.partition(detailDTOList, 20);
            for (List<KiteBatchAcceptDetailDTO> detailList : partitionList) {
                LOGGER.info("partitionList:" + JSONObject.toJSONString(detailList));
                CompletableFuture.runAsync(() -> {
                    try {
                        for (KiteBatchAcceptDetailDTO detailDTO : detailList) {
                            LOGGER.info("single detailDTO:" + JSONObject.toJSONString(detailDTO));
                            // 开启缓存事务
                            KiteTransactionUtils.beginTransaction();
                            if (KiteStringUtils.isBlank(KiteTransactionUtils.getTransactionId())) {
                                String transactionId = KiteTransactionUtils.genTransactionId();
                                LOGGER.info("genTransactionId:" + transactionId);
                                KiteTransactionUtils.setTransactionId(transactionId);
                            }
                            DataResult<String> result = new DataResult<>();
                            Example example = new Example(KiteBatchAcceptDetail.class);
                            example.createCriteria().andEqualTo("detailId", detailDTO.getDetailId())
                                .andEqualTo("batchId", detailDTO.getBatchId());
                            try {
                                LoginInfo loginInfo =
                                    KiteMapUtils.getValue(loginInfoMap, detailDTO.getCreateStaff().toString());
                                if (Objects.isNull(loginInfo)) {
                                    loginInfo = BatchAcceptUtil.getLoginInfo(detailDTO);
                                    loginInfoMap.put(detailDTO.getCreateStaff().toString(), loginInfo);
                                }
                                ContextUtil.setLoginInfo2LoginInfoThreadLocal(loginInfo);
                                // 在缓存事务中重新加载场景，防止场景缓存失效
                                KiteSceneInst mainKiteSceneInst = querySceneInstService.getSceneInst(detailDTO.getSceneInstId(),
                                    detailDTO.getCustId());
                                // 查客户详情（为了避免多次调接口，同一个客户根据客户id用map保存）
                                CuccContextCustDTO contextCust = custInfoMap.get(detailDTO.getCustId());
                                if (Objects.isNull(contextCust)) {
                                    contextCust = new CuccContextCustDTO();
                                    CuccCustInfoDetailDTO cuccCustInfoDTO =
                                        custReceptionService.getCustomerInfoNew(detailDTO.getCustId());
                                    if (Objects.nonNull(cuccCustInfoDTO)) {
                                        KiteBeanUtils.copyProperties(cuccCustInfoDTO, contextCust);
                                        contextCust.setLanId(cuccCustInfoDTO.getCityCode());
                                        contextCust.setRegionId(cuccCustInfoDTO.getBcityCode());
                                        contextCust.setCustType(cuccCustInfoDTO.getCustClassType());
                                        contextCust.setCbssId(cuccCustInfoDTO.getCustId());
                                    }
                                    custInfoMap.put(detailDTO.getCustId(), contextCust);
                                    ServiceContext.setCust(contextCust.getCustId(),
                                        BatchAcceptUtil.transToCustDTO(contextCust));
                                    CuccContextCustCache.put(ContextUtil.getLoginInfo().getUserInfo().getUserCode(),
                                        contextCust.getCustId(), contextCust);
                                }
                                // 设置批量标识
                                // ServiceContext.setIsBatchAccept(Boolean.TRUE);
                                // 初始化子单场景，计算主单修改信息并复制到子单
                                StopWatch stopWatch = new StopWatch("copyChange");
                                stopWatch.start("copyChange");
                                JSONObject orderInfo = JSONObject.parseObject(detailDTO.getAttrJson());
                                if (Objects.nonNull(detailDTO.getOfferInstId())) {
                                    addCommonInst(mainKiteSceneInst.getCustId(), mainKiteSceneInst.getRootInstId(),
                                        detailDTO.getOfferInstId().toString(), mainKiteSceneInst.getSceneInstId());
                                    String flowId = KiteMapUtils.getString(orderInfo, "flowId");
                                    String taskId = KiteMapUtils.getString(orderInfo, "taskId");
                                    String taskCode = querySceneInstService
                                        .getExtParameterValue(mainKiteSceneInst.getSceneInstId(), "taskCode");
                                    // 调接口签订
                                    result = orderSignService.overOrderTaskBatch(mainKiteSceneInst.getSceneInstId(),
                                        detailDTO.getCreateStaff().toString(), detailDTO.getCreateOrgId(), flowId,
                                        taskCode, taskId, dealResult, null, contextCust,
                                        detailDTO.getOfferInstId().toString());
                                } else {
                                    String subSceneInstId =
                                        createScene(orderInfo, detailDTO, mainKiteSceneInst, loginInfo);
                                    replaceMainOrder(mainKiteSceneInst,
                                        querySceneInstService.getSceneInst(subSceneInstId), importType);
                                    stopWatch.stop();
                                    System.out.println(stopWatch.prettyPrint());
                                    String worksheetId =
                                        querySceneInstService.getExtParameterValue(subSceneInstId, "worksheetId");
                                    String taskCode =
                                        querySceneInstService.getExtParameterValue(subSceneInstId, "taskCode");
                                    String taskId =
                                        querySceneInstService.getExtParameterValue(subSceneInstId, "taskId");
                                    operateSceneInstService.putExtParameter(subSceneInstId, "batchNode", "T");
                                    // 拼装报文前提交一下缓存，防止缓存取不到
                                    long start = System.currentTimeMillis();
                                    KiteTransactionUtils.commit();
                                    long end = System.currentTimeMillis();
                                    System.out
                                        .println("*************** 提交缓存，耗时[" + (end - start) + "]ms ***************");
                                    // 调接口签订
                                    result = orderSignService.overOrderTaskBatch(subSceneInstId,
                                        detailDTO.getCreateStaff().toString(), detailDTO.getCreateOrgId(), worksheetId,
                                        taskCode, taskId, dealResult, null, contextCust, null);
                                }

                            } catch (Exception e) {
                                LOGGER.info("single task exception-->");
                                LOGGER.error(e);
                                result.setResultObject(ExceptionUtils.getStackTrace(e));
                            } finally {
                                LOGGER.info("批量受理更新处理结果：");
                                LocalDateTime now = LocalDateTime.now();
                                DataResult<String> finalResult = result;
                                TransactionUtils.executeNew(() -> {
                                    if (KiteStringUtils.isEqual(ErrorConsts.SUCCESS.getCode(),
                                        finalResult.getResultCode())) {
                                        // 成功
                                        KiteBatchAcceptDetail detail =
                                            new KiteBatchAcceptDetail().setDealResult(finalResult.getResultObject())
                                                .setStatusCd("1200").setStatusDate(now);
                                        detailDTO.setStatusCd("1200");
                                        kiteBatchAcceptDetailMapper.updateByExampleSelective(detail, example);
                                    } else {
                                        // 失败
                                        KiteBatchAcceptDetail detail =
                                            new KiteBatchAcceptDetail().setDealResult(finalResult.getResultObject())
                                                .setStatusCd("1100").setStatusDate(now);
                                        detailDTO.setStatusCd("1100");
                                        kiteBatchAcceptDetailMapper.updateByExampleSelective(detail, example);
                                    }
                                });
                                ContextUtil.removeThreadLocal();
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.info("all partitionList exception:");
                        LOGGER.error(e);
                        try {
                            TransactionUtils.executeNew(() -> {
                                LocalDateTime now = LocalDateTime.now();
                                List<KiteBatchAcceptDetail> kiteBatchAcceptDetailList =
                                    detailList.stream().map(list -> {
                                        KiteBatchAcceptDetail kiteBatchAcceptDetail = new KiteBatchAcceptDetail();
                                        kiteBatchAcceptDetail.setDealResult(ExceptionUtils.getStackTrace(e))
                                            .setStatusCd("1100").setStatusDate(now)
                                            .setBatchId(list.getBatchId()).setDetailId(list.getDetailId());
                                        return kiteBatchAcceptDetail;
                                    }).collect(Collectors.toList());
                                kiteBatchAcceptDetailMapper
                                    .updateKiteBatchAcceptDetailStatusAndOrderId(kiteBatchAcceptDetailList);
                            });
                        } catch (Exception ex) {
                            System.out.println("update partitionList exception:");
                            LOGGER.error(ex);
                        }
                    }
                }, executorService);
            }
        } catch (Exception e) {
            LOGGER.info("all list exception:");
            LOGGER.error(e);
            TransactionUtils.executeNew(() -> {
                List<KiteBatchAcceptDetail> kiteBatchAcceptDetailList = detailDTOList.stream().map(list -> {
                    KiteBatchAcceptDetail kiteBatchAcceptDetail = new KiteBatchAcceptDetail();
                    kiteBatchAcceptDetail.setDealResult(ExceptionUtils.getStackTrace(e)).setStatusCd("1100")
                        .setStatusDate(LocalDateTime.now()).setBatchId(list.getBatchId())
                        .setDetailId(list.getDetailId());
                    return kiteBatchAcceptDetail;
                }).collect(Collectors.toList());
                kiteBatchAcceptDetailMapper.updateKiteBatchAcceptDetailStatusAndOrderId(kiteBatchAcceptDetailList);
            });
        }
    }

    private void replaceMainOrder(KiteSceneInst mainSceneInst, KiteSceneInst subSceneInst, String importType) {
        if (KiteStringUtils.equals(LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7100, importType)) {
            replaceSign(mainSceneInst, subSceneInst);
        }
        else if (KiteStringUtils.equals(LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7200, importType)) {
            replaceStartRent(mainSceneInst, subSceneInst);
        }
    }

    private void replaceSign(KiteSceneInst mainSceneInst, KiteSceneInst subSceneInst) {
        // 对于签订，没有修改信息，都是新增的，暂时先处理新增的
        String mainSceneInstId = mainSceneInst.getSceneInstId();
        String subSceneInstId = subSceneInst.getSceneInstId();
        String custId = mainSceneInst.getCustId();
        String mainRootInstId = mainSceneInst.getRootInstId();
        String subRootInstId = subSceneInst.getRootInstId();

        addCommonInst(custId, mainRootInstId, subRootInstId, subSceneInstId);

        // 按模块将主单的公共属性设置到子单 订单93、联系人信息61、发展人信息62、经办人信息64、代理人信息63、附件及备注94、用户信息模块 113
        List<KiteAttr> replaceCommonAttrs =
            querySceneInstService.listCommonAttrsByModuleIds(mainSceneInstId,
                Lists.newArrayList(LocalKeyConsts.MODULE_ID_ORDER, LocalKeyConsts.MODULE_ID_CONTACT,
                    LocalKeyConsts.MODULE_ID_DEVELOPER, LocalKeyConsts.MODULE_ID_OPERATOR,
                    LocalKeyConsts.MODULE_ID_AGENT, LocalKeyConsts.MODULE_ID_ATTACHMENT, LocalKeyConsts.MODULE_ID_GRP_USER));
        operateSceneInstService.setCommonAttrValues(subSceneInstId,
            KiteBeanUtils.copyPropsForList(replaceCommonAttrs, KiteAttrDTO.class));
    }

    /**
     * 主单调订单更新接口更新订单
     * 记录子单到批量表
     * @param sceneInstId
     * @return
     */
    @Override
    public String batchSign(String sceneInstId, CuccContextCustDTO custDto) {
        LoginInfo loginInfo = ContextUtil.getLoginInfo();
        UserInfo userInfo = loginInfo.getUserInfo();
        String staffId = userInfo.getUserId().toString();
        String orgId = loginInfo.getOrgInfo().getOrgId().toString();
        Map<String, Object> orderInfo = querySceneInstService.getExtParameterValue(sceneInstId, "batchOrderInfo");
        List<Map<String, Object>> subOrderList = KiteMapUtils.getValue(orderInfo, "workSheetData");
        if (KiteListUtils.isEmpty(subOrderList)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("批量签订失败，缺少子单数据");
        }
        KiteSceneInst sceneInst = querySceneInstService.getSceneInst(sceneInstId);
        operateSceneInstService.putExtParameter(sceneInstId, "orderId", KiteMapUtils.getString(orderInfo, "rootOrderId"));
        // 主单调订单更新接口更新订单
        KitePosDTO posDto = posServiceHandler.deal(sceneInstId, staffId, orgId, custDto, false, false, null);
        KitePosDTO mainPosDto = ExtOrderInfoCache.get(sceneInstId);
        replacePosDTO(mainPosDto, posDto);
        sceneCommitService.commitModifyScene(sceneInstId, staffId, orgId, custDto, posDto);
        Long batchId = this.insertBatchAccept(sceneInstId, subOrderList, "",
            LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7100, String.valueOf(subOrderList.size()));
        // 状态更新为提交处理中
        kiteBatchAcceptMapper.updateByPrimaryKeySelective(
            new KiteBatchAccept().setBatchId(batchId).setStatusCd("1400")
                .setStatusDate(LocalDateTime.now()));
        operateSceneInstService.putExtParameter(sceneInstId, "batchNodeId", batchId);
        this.cacheShoppingCartGroup(sceneInstId, sceneInst.getCartId(), sceneInst.getCustId());
        return "批量签订成功，批次号为：" + batchId;
    }

    @Override
    public List<String> clickStartRentOrderList(List<String> orderIds, String sceneInstId) {
        Map<String, Object> orderInfo = querySceneInstService.getExtParameterValue(sceneInstId, "batchOrderInfo");
        List<Map<String, Object>> subOrderList = KiteMapUtils.getValue(orderInfo, "workSheetData");
        if (KiteListUtils.isEmpty(subOrderList)) {
            return null;
        }
        List<Map<String, Object>> starRentOrderList =
            subOrderList.stream().filter(subOrder -> orderIds.contains(KiteMapUtils.getString(subOrder, "orderId")))
                .collect(Collectors.toList());
        operateSceneInstService.putExtParameter(sceneInstId, "starRentOrderList", starRentOrderList);
        return starRentOrderList.stream().map(order -> KiteMapUtils.getString(order, "orderId"))
            .collect(Collectors.toList());
    }

    /**
     * 主单调订单更新接口更新订单 ORDER_START_RENT
     * 记录子单到批量表
     * @param sceneInstId
     * @return
     */
    @Override
    public String batchStartRent(String sceneInstId, CuccContextCustDTO custDto) {
        // 校验起租时间
        KiteAttr startRentTimeAttr = querySceneInstService.getCommonAttr(sceneInstId, "REC_10057");
        if (Objects.nonNull(startRentTimeAttr) && KiteStringUtils.isEmpty(startRentTimeAttr.getValue())) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("批量起租失败，起租时间不能未空");
        }
        List<Map<String, Object>> subOrderList = querySceneInstService.getExtParameterValue(sceneInstId, "starRentOrderList");
        if (KiteListUtils.isEmpty(subOrderList)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("批量起租失败，缺少子单数据");
        }
        KiteSceneInst sceneInst = querySceneInstService.getSceneInst(sceneInstId);
        Long batchId = this.insertBatchAccept(sceneInstId, subOrderList, "",
            LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7200, String.valueOf(subOrderList.size()));

        // 状态更新为提交处理中
        kiteBatchAcceptMapper.updateByPrimaryKeySelective(
            new KiteBatchAccept().setBatchId(batchId).setStatusCd("1400")
                .setStatusDate(LocalDateTime.now()));
        operateSceneInstService.putExtParameter(sceneInstId, "batchNodeId", batchId);
        this.cacheShoppingCartGroup(sceneInstId, sceneInst.getCartId(), sceneInst.getCustId());
        return "批量起租成功，批次号为：" + batchId;
    }

    @Override
    public boolean batchStartRentJob() {
        StopWatch stopWatch = new StopWatch("startRent");
        stopWatch.start("startRent");

        batchOrderTask(LocalKeyConsts.BATCH_ACCEPT_IMPORT_TYPE_7200, "ORDER_START_RENT");

//        String batchId = curBatchId.get();
//        updateBatchStatus(batchId);
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
        return Boolean.TRUE;
    }

    @Override
    public String instantiateSubOrder(String sceneInstId, WorksheetMergeVO worksheetMergeVO) {
        KiteSceneInst sceneInst = querySceneInstService.getSceneInst(sceneInstId);
        Map<String, Object> subOrderInstMap = querySceneInstService.getExtParameterValue(sceneInstId, "instSubOrderMap");
        String subOfferInst = KiteMapUtils.getString(subOrderInstMap, worksheetMergeVO.getFlowId());
        if (KiteStringUtils.isNotEmpty(subOfferInst)) {
            operateSceneInstService.putExtParameter(sceneInst.getSceneInstId(), "curOfferInstId", subOfferInst);
            return "400";
        }
        String custId = sceneInst.getCustId();
        // 获取工单信息
        Map<String, Object> dataMap = this.cuccSceneService
            .getOfferSceneDataByWorkOrder(worksheetMergeVO.getOrderId(), worksheetMergeVO.getFlowId(), null,
                sceneInst.getServiceOfferId(), sceneInst.getOperType(), "", "", ContextUtil.getUserId().toString(),
                ContextUtil.getLandId(), ContextUtil.getLoginInfo().getUserInfo().getPostRegionId().toString(), null)
            .getResultObject();

        LoginInfo loginInfo = ContextUtil.getLoginInfo();
        Map<String, Object> loginExtParams = loginInfo.getUserInfo().getExtParams();
        String staffId = loginInfo.getUserInfo().getUserId() + "";
        // 特殊参数,用于创建场景后设置场景参数用,该方法结束前移除
        String nodeCode = KiteMapUtils.getString(dataMap, "nodeCode");
        loginExtParams.put("nodeCode", nodeCode);
        loginExtParams.put("serviceOfferId", KiteMapUtils.getString(dataMap, "serviceOfferId"));
        loginExtParams.put("goodsNodeId", KiteMapUtils.getString(dataMap, "goodsNodeId"));
        KitePosDTO kitePosDTO = (KitePosDTO) KiteMapUtils.getObject(dataMap, "kitePosDTO");
        String offerInstId = this.initSceneOfferInstService.initBatchSignOfferInst(sceneInst, custId, staffId, kitePosDTO.getGoods(), loginInfo);
        operateSceneInstService.putExtParameter(sceneInst.getSceneInstId(), "curOfferInstId", offerInstId);
        operateSceneInstService.putExtParameter(sceneInst.getSceneInstId(), "batchSignUser_" + offerInstId, kitePosDTO.getUsers());
        List<KiteProdInst> kiteProdInsts = querySceneInstService.listProdInstsFromOfferInst(custId, offerInstId);
        localBatchAcceptBO.specialDealSignAttr(kiteProdInsts.get(0), sceneInstId, kitePosDTO.getUsers().get(0).getSerialNumber());
        return "400";
    }

    @Override
    public boolean saveModification(String sceneInstId, String flowId) {
        String offerInstId = querySceneInstService.getExtParameterValue(sceneInstId, "curOfferInstId");
        // 将offerInstId存到缓存中
        Map<String, Object> subOrderInstMap = querySceneInstService.getExtParameterValue(sceneInstId, "instSubOrderMap");
        if (KiteMapUtils.isEmpty(subOrderInstMap)) {
            subOrderInstMap = new HashMap<>();
        }
        subOrderInstMap.put(flowId, offerInstId);
        operateSceneInstService.putExtParameter(sceneInstId, "instSubOrderMap", subOrderInstMap);
        operateSceneInstService.removeExtParameter(sceneInstId, "curOfferInstId");
        return Boolean.TRUE;
    }

    /**
     * 取消实例化（取消修改）
     *
     * @param sceneInstId
     * @param custId
     * @return
     */
    @Override
    public Boolean cancelModification(String sceneInstId, String custId, String flowId) {
        String offerInstId = querySceneInstService.getExtParameterValue(sceneInstId, "curOfferInstId");
        if (KiteStringUtils.isNotEmpty(offerInstId)) {
            Map<String, Object> subOrderInstMap = querySceneInstService.getExtParameterValue(sceneInstId, "instSubOrderMap");
            String subOfferInst = KiteMapUtils.getString(subOrderInstMap, flowId);
            // 实例ID没有入库的才删
            if (KiteStringUtils.isEmpty(subOfferInst)) {
                batchAcceptService.deleteOfferInstAndRelInsts(sceneInstId, custId, offerInstId);
                operateSceneInstService.removeExtParameter(sceneInstId, "batchSignUser_" + offerInstId);
            }
            operateSceneInstService.removeExtParameter(sceneInstId, "curOfferInstId");
        }
        return Boolean.TRUE;
    }

    @Override
    public void restoreRentInfo(String sceneInstId) {
        if (!BatchAcceptUtil.isBatchStartRent(sceneInstId)) {
            return;
        }
        List<Map<String, Object>> flowDataList = querySceneInstService.getExtParameterValue(sceneInstId, "flowData");
        if (KiteListUtils.isEmpty(flowDataList)) {
            return;
        }
        Map<String, Object> flowDataMap = flowDataList.stream()
                .filter(flowData -> KiteListUtils.isNotEmpty(JSONObject.parseArray(KiteMapUtils.getString(flowData, "FLOW_VALUE"))))
                .findFirst().orElse(null);
        if (KiteMapUtils.isEmpty(flowDataMap)) {
            return;
        }
        JSONArray flowValueAttr = JSONObject.parseArray(KiteMapUtils.getString(flowDataMap, "FLOW_VALUE"));
        flowValueAttr.forEach(attr -> {
            JSONObject attrJSON = JSONObject.parseObject(JSONObject.toJSONString(attr));
            String fieldName = KiteMapUtils.getString(attrJSON, "code");
            String value = KiteMapUtils.getString(attrJSON, "value");
            // 附件在订单属性，此处不处理
            if (KiteStringUtils.equals(fieldName, "REC_10046")) {
                return;
            }
            operateSceneInstService.setCommonAttrValue(sceneInstId, fieldName, value, value);
        });
    }

    private void replaceStartRent(KiteSceneInst mainSceneInst, KiteSceneInst subSceneInst) {
        // 按模块将主单的公共属性设置到子单 订单93 起租模块203
        List<KiteAttr> replaceCommonAttrs = querySceneInstService.listCommonAttrsByModuleIds(
            mainSceneInst.getSceneInstId(), Lists.newArrayList(LocalKeyConsts.MODULE_ID_ORDER, "203"));
        operateSceneInstService.setCommonAttrValues(subSceneInst.getSceneInstId(),
            KiteBeanUtils.copyPropsForList(replaceCommonAttrs, KiteAttrDTO.class));
    }

    private void replacePosDTO(KitePosDTO mainPosDTO, KitePosDTO scenePosDTO) {
        // 设置goods节点实例、资费等信息
        mainPosDTO.getGoods().forEach(posGoods -> {
            if (KiteListUtils.isNotEmpty(scenePosDTO.getContract())) {
                posGoods.setContractInstId(scenePosDTO.getContract().get(0).getInstanceId());
            }
            if (KiteListUtils.isNotEmpty(scenePosDTO.getAccounts())) {
                posGoods.setAccountInstId(scenePosDTO.getAccounts().get(0).getInstanceId());
            }
            if (KiteListUtils.isNotEmpty(scenePosDTO.getDiscount())) {
                posGoods.setDiscountInstId(scenePosDTO.getDiscount().get(0).getInstanceId());
            }
            if (KiteListUtils.isNotEmpty(scenePosDTO.getGoods())) {
                List<KitePosGoodsFeeDTO> fees = scenePosDTO.getGoods().stream()
                    .filter(goods -> KiteStringUtils.equals(goods.getCode(), posGoods.getCode())).findFirst()
                    .map(KitePosGoodsDTO::getFees).orElse(null);
                posGoods.setFees(KiteObjectUtils.clone(fees));
                List<KitePosGoodsPayRealtionDTO> payRealtionList = scenePosDTO.getGoods().stream()
                    .filter(goods -> KiteStringUtils.equals(goods.getCode(), posGoods.getCode())).findFirst()
                    .map(KitePosGoodsDTO::getPayRelation).orElse(null);
                posGoods.setPayRelation(KiteObjectUtils.clone(payRealtionList));
            }
        });
        scenePosDTO.setGoods(mainPosDTO.getGoods());
        scenePosDTO.setUsers(mainPosDTO.getUsers());
    }

    private void addCommonInst(String custId, String mainRootInstId, String subRootInstId, String subSceneInstId) {
        KiteOfferInst mainRootOfferInst = querySceneInstService.getOfferInst(custId, mainRootInstId);
        String offerId = mainRootOfferInst.getOfferId();
        // 设置付费关系
        List<CuccKiteOfferInstAcct> rootKiteOfferInstAccts = operateOfferInstAcctRelService.listOfferInstAccts(custId, mainRootInstId);
        if (KiteListUtils.isNotEmpty(rootKiteOfferInstAccts)) {
            List<CuccKiteOfferInstAcct> kiteOfferInstAccts =
                KiteBeanUtils.copyPropsForList(rootKiteOfferInstAccts, CuccKiteOfferInstAcct.class);
            for (CuccKiteOfferInstAcct rootKiteOfferInstAcct : kiteOfferInstAccts) {
                CuccKiteOfferInstAcct kiteOfferInstAcct = KiteBeanUtils
                    .transform(rootKiteOfferInstAcct, CuccKiteOfferInstAcct.class);
                kiteOfferInstAcct.setOfferInstId(subRootInstId);
                kiteOfferInstAcct.setInstId(IdUtils.generateInstId(InstType.PROD_INST_ACCT));

                operateSceneInstService
                    .addOfferInstAcct(subSceneInstId, kiteOfferInstAcct, subRootInstId, RelType.OFFER_OBJ_REL_TYPE_170000,
                        ActionType.A, KeyConsts.OFFER_ACCOUNT_ROLE, offerId);
            }
        }
        else {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("付费关系不能为空");
        }

        // 设置合同
        List<KiteOfferInstContract> offerInstContracts = querySceneInstService.listOfferInstContracts(custId, mainRootInstId);
        if (KiteListUtils.isNotEmpty(offerInstContracts)) {
            for (KiteOfferInstContract rootOfferInstContract : offerInstContracts) {
                KiteOfferInstContract offerInstContract = KiteBeanUtils.transform(rootOfferInstContract, KiteOfferInstContract.class);
                offerInstContract.setOfferInstId(subRootInstId);
                offerInstContract.setInstId(IdUtils.generateInstId(InstType.PROD_INST_ACCT));
                operateSceneInstService.addOfferInstContract(subSceneInstId, offerInstContract, subRootInstId,
                    RelType.OFFER_OBJ_REL_TYPE_190000, ActionType.A, KeyConsts.OFFER_CONTRACT_ROLE, offerId);
            }
        }
        else {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("合同不能为空");
        }

        // 设置折扣审批
        List<KiteOfferInstDiscount> discountInfos = querySceneInstService.listOfferInstDiscounts(custId, mainRootInstId);
        if (KiteListUtils.isNotEmpty(discountInfos)) {
            List<KiteOfferInstDiscount> kiteOfferInstDiscounts = discountInfos.stream().map(rootOfferInstDiscount -> {
                KiteOfferInstDiscount kiteOfferInstDiscount = KiteBeanUtils.transform(rootOfferInstDiscount, KiteOfferInstDiscount.class);
                kiteOfferInstDiscount.setOfferInstId(subRootInstId);
                return kiteOfferInstDiscount;
            }).collect(Collectors.toList());
            operateOfferInstDiscountService
                .entryOfferInstDiscount(subSceneInstId, custId, subRootInstId, JSON.toJSONString(kiteOfferInstDiscounts));
        }

        // 设置资费
        List<KiteOfferInst> rootAdditionalOfferInsts = querySceneInstService.listAdditionalOfferInstsFromOfferInst(custId, mainRootInstId);
        if (KiteListUtils.isEmpty(rootAdditionalOfferInsts)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("资费不能为空");
        }
        for (KiteOfferInst rootAdditionalOfferInst : rootAdditionalOfferInsts) {
            KiteOfferInst additionalOfferInst = KiteBeanUtils.transform(rootAdditionalOfferInst, KiteOfferInst.class);
            additionalOfferInst.setOfferInstId(subRootInstId);
            AcceptScalpelUtil.replaceInstId(additionalOfferInst, subRootInstId);
            operateSceneInstService.addAdditionalOfferInstToOfferInst(subSceneInstId, rootAdditionalOfferInst, subRootInstId,
                RelType.OFFER_REL_TYPE_100000, ActionType.A);
        }
    }

    public Long insertBatchAccept(String sceneInstId, List<?> dataList, String fileName, String importType, String rowsNum) {
        // 写批量业务受理文件表
        Long batchId = Long.valueOf(SeqUtils.getNext("SEQ_KITE_BATCH_ACCEPT_BATCH_ID"));
        batchAcceptService.insertKiteBatchAccept(sceneInstId, fileName, rowsNum, importType, null, batchId);
        // 写批量业务受理明细表
        insertKiteBatchAcceptDetail(batchId, dataList, sceneInstId);
        return batchId;
    }

    private void insertKiteBatchAcceptDetail(Long batchId, List<?> dataList, String sceneInstId) {
        List<KiteBatchAcceptDetail> kiteBatchAcceptDetails = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        List<String> checkList = Lists.newArrayList();
        for (int i = 0; i < dataList.size(); i++) {
            Map<String, Object> o = (Map<String, Object>) dataList.get(i);
            KiteBatchAcceptDetail kiteBatchAcceptDetail = new KiteBatchAcceptDetail()
                .setDetailId(Long.valueOf(SeqUtils.getNext("SEQ_KITE_BATCH_ACCEPT_DETAIL_DETAIL_ID")))
                .setBatchId(batchId).setLineNum(i + 2).setAttrJson(JSON.toJSONString(o)).setOfferInstId(null)
                .setExtOrderId(KiteMapUtils.getString(o, "orderId")).setCheckStatus("1000").setCheckResult("")
                .setStatusCd("1000").setStatusDate(now).setDealResult("");

            String flowId = KiteMapUtils.getString(o, "flowId");
            Map<String, Object> subOrderInstMap = querySceneInstService.getExtParameterValue(sceneInstId, "instSubOrderMap");
            String subOfferInst = KiteMapUtils.getString(subOrderInstMap, flowId);
            if (KiteStringUtils.isNotEmpty(subOfferInst)) {
                kiteBatchAcceptDetail.setOfferInstId(Long.valueOf(subOfferInst));
            }
            if (checkList.contains(JSON.toJSONString(o))) {
                kiteBatchAcceptDetail.setCheckStatus("1100").setCheckResult("校验失败：导入数据重复，请检查");
            }
            checkList.add(JSON.toJSONString(o));
            kiteBatchAcceptDetails.add(kiteBatchAcceptDetail);
        }
        if (KiteListUtils.isNotEmpty(kiteBatchAcceptDetails)) {
            kiteBatchAcceptDetailMapper.insertBatch(kiteBatchAcceptDetails);
        }
    }

    private void cacheShoppingCartGroup(String sceneInstId, String cartId, String custId) {
        final String userId = ContextUtil.getUserId().toString();
        final String sessionId = ContextUtil.getHttpSessoinId();
        LoginInfo loginInfo = ContextUtil.getLoginInfo();
        SimpleLoginInfo simpleLoginInfo = ContextUtil.getSimpleLoginInfo();
        if (KiteStringUtils.isEmpty(cartId)) {
            cartId = SpringUtil.getBean(ICuccQueryShoppingCartService.class).getShoppingCartId(userId, custId);
            KiteSceneInst kiteSceneInst = querySceneInstService.getSceneInst(sceneInstId);
            kiteSceneInst.setCartId(cartId);
            operateSceneInstService.setSceneInst(kiteSceneInst);
        }
        String cartStr = cartId;
        String transactionId = KiteTransactionUtils.getTransactionId();
        CompletableFuture.runAsync(() -> {
            KiteTransactionUtils.setTransactionId(transactionId);
            try {
                ContextUtil.setHttpSessoinId(sessionId);
                // 兼容接口调用
                ContextUtil.setLoginInfo2LoginInfoThreadLocal(loginInfo);
                ContextUtil.putSimpleLoginInfo2ThreadLocal(simpleLoginInfo);
                SpringUtil.getBean(ICuccOperateShoppingCartService.class)
                    .cacheShoppingCartGroup(ContextUtil.getSimpleLoginInfo(), sceneInstId, userId, custId,
                        KeyConsts.SHOPPING_CART_GROUP_STATUS_INPUT, cartStr);
            }
            catch (Exception e) {
                // just log
                LOGGER.error(e);
            }
            finally {
                KiteTransactionUtils.commit();
                ContextUtil.removeThreadLocal();
            }
        });
    }
}
